// 导入类型定义
import { ErrorInfo, SDKConfig } from '../types'; // 导入错误信息和SDK配置类型
import {
  getCurrentUrl, // 获取当前页面URL
  getUserAgent, // 获取用户代理
  formatErrorStack, // 格式化错误堆栈
  shouldSample // 判断是否采样
} from '../utils'; // 导入工具函数

// 错误监控器类，负责采集和处理各种错误
export class ErrorMonitor {
  private config: SDKConfig; // SDK配置
  private errorHandler: (error: ErrorInfo) => void; // 错误处理回调

  // 保存原始方法，以便在destroy时恢复
  private originalConsoleError: (...args: any[]) => void;
  private originalConsoleWarn: (...args: any[]) => void;
  private originalXHROpen: (method: string, url: string | URL, async?: boolean, username?: string | null, password?: string | null) => void;
  private originalXHRSend: (body?: Document | XMLHttpRequestBodyInit | null) => void;
  private originalFetch: (input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>;

  // 构造函数，传入配置和回调
  constructor(config: SDKConfig, errorHandler: (error: ErrorInfo) => void) {
    this.config = config; // 保存配置
    this.errorHandler = errorHandler; // 保存回调

    // 在构造时保存原始方法
    this.originalConsoleError = console.error;
    this.originalConsoleWarn = console.warn;
    this.originalXHROpen = XMLHttpRequest.prototype.open;
    this.originalXHRSend = XMLHttpRequest.prototype.send;
    this.originalFetch = window.fetch;
  }

  // --- Event Handlers (as class properties to preserve 'this' and for removal) ---

  private jsErrorHandler = (event: ErrorEvent): void => {
    // 资源加载错误在此也会被捕获，但我们希望在 resourceErrorHandler 中处理
    // 如果 event.target 是一个元素且不是 window，则认为是资源加载错误
    if (event.target && event.target !== window) {
      return;
    }

    if (!shouldSample(this.config.sampleRate)) {
      return; // 不采样则返回
    }

    const errorInfo: ErrorInfo = {
      type: 'js', // 错误类型
      message: event.message || 'Unknown JavaScript error', // 错误信息
      stack: event.error?.stack || '', // 堆栈信息
      filename: event.filename || '', // 文件名
      lineno: event.lineno || 0, // 行号
      colno: event.colno || 0, // 列号
      timestamp: Date.now(), // 时间戳
      url: getCurrentUrl(), // 当前URL
      userAgent: getUserAgent(), // 用户代理
      extra: {
        error: event.error // 原始错误对象
      }
    };
    this.errorHandler(errorInfo); // 上报错误
  }

  private promiseErrorHandler = (event: PromiseRejectionEvent): void => {
    if (!shouldSample(this.config.sampleRate)) {
      return; // 不采样则返回
    }
    const errorInfo: ErrorInfo = {
      type: 'promise', // 错误类型
      message: event.reason?.message || event.reason || 'Unhandled Promise rejection', // 错误信息
      stack: event.reason?.stack || '', // 堆栈信息
      timestamp: Date.now(), // 时间戳
      url: getCurrentUrl(), // 当前URL
      userAgent: getUserAgent(), // 用户代理
      extra: {
        reason: event.reason // 原始reason
      }
    };
    this.errorHandler(errorInfo); // 上报错误
  }

  private resourceErrorHandler = (event: ErrorEvent): void => {
    // 只处理资源加载错误, 通过 event.target 区分
    if (event.target && event.target !== window) {
      if (!shouldSample(this.config.sampleRate)) {
        return; // 不采样则返回
      }
      const target = event.target as HTMLElement; // 资源元素
      const errorInfo: ErrorInfo = {
        type: 'resource', // 错误类型
        message: `Failed to load resource: ${target.tagName}`, // 错误信息
        timestamp: Date.now(), // 时间戳
        url: getCurrentUrl(), // 当前URL
        userAgent: getUserAgent(), // 用户代理
        extra: {
          tagName: target.tagName, // 标签名
          src: (target as HTMLImageElement).src || (target as HTMLScriptElement).src || (target as HTMLLinkElement).href, // 资源地址
          outerHTML: target.outerHTML, // 元素outerHTML
        }
      };
      this.errorHandler(errorInfo); // 上报错误
    }
  }

  // 初始化错误监控
  public init(): void {
    this.setupJsErrorHandler(); // 监听JS错误
    this.setupPromiseErrorHandler(); // 监听Promise错误
    this.setupResourceErrorHandler(); // 监听资源加载错误
    this.setupHttpErrorHandler(); // 监听HTTP请求错误
    if (this.config.enableConsole) {
      this.setupConsoleErrorHandler(); // 监听控制台错误
    }
  }

  // 设置JavaScript错误处理器
  private setupJsErrorHandler(): void {
    window.addEventListener('error', this.jsErrorHandler, true); // 捕获阶段
  }

  // 设置Promise错误处理器
  private setupPromiseErrorHandler(): void {
    window.addEventListener('unhandledrejection', this.promiseErrorHandler);
  }

  // 设置资源加载错误处理器
  private setupResourceErrorHandler(): void {
    // 资源加载错误也会触发 window.onerror, 在捕获阶段监听以优先处理
    window.addEventListener('error', this.resourceErrorHandler, true); // 捕获阶段
  }

  // 设置HTTP请求错误处理器
  private setupHttpErrorHandler(): void {
    // 拦截XMLHttpRequest
    const self = this; // 保存this指针
    XMLHttpRequest.prototype.open = function (method: string, url: string, async: boolean = true, username?: string | null, password?: string | null) {
      (this as any)._xhrMethod = method; // 记录请求方法
      (this as any)._xhrUrl = url; // 记录请求URL
      return self.originalXHROpen.call(this, method, url, async, username, password); // 调用原始方法
    };
    XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {
      this.addEventListener('error', () => { // XHR 网络错误
        if (!shouldSample(self.config.sampleRate)) { return; }
        const errorInfo: ErrorInfo = {
          type: 'http', message: `XHR Error: ${(this as any)._xhrMethod} ${(this as any)._xhrUrl}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
          extra: { method: (this as any)._xhrMethod, url: (this as any)._xhrUrl, status: this.status, statusText: this.statusText }
        };
        self.errorHandler(errorInfo);
      });
      this.addEventListener('load', () => { // 检查非2xx的响应
        if (this.status >= 400 && this.status < 600) {
          if (!shouldSample(self.config.sampleRate)) { return; }
          const errorInfo: ErrorInfo = {
            type: 'http', message: `XHR Error: ${this.status} at ${(this as any)._xhrUrl}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
            extra: { method: (this as any)._xhrMethod, url: (this as any)._xhrUrl, status: this.status, statusText: this.statusText }
          };
          self.errorHandler(errorInfo);
        }
      });
      return self.originalXHRSend.call(this, body); // 调用原始send
    };

    // 拦截Fetch API
    window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
      const response = await self.originalFetch(input, init).catch(error => {
        if (shouldSample(self.config.sampleRate)) {
          const errorInfo: ErrorInfo = {
            type: 'http', message: `Fetch Network Error: ${error.message}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
            extra: { method: init?.method || 'GET', url: typeof input === 'string' ? input : (input as Request).url, error: error }
          };
          self.errorHandler(errorInfo);
        }
        throw error;
      });
      if (!response.ok) {
        if (shouldSample(this.config.sampleRate)) {
          const errorInfo: ErrorInfo = {
            type: 'http', message: `Fetch Error: ${response.status} ${response.statusText}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
            extra: { method: init?.method || 'GET', url: response.url, status: response.status, statusText: response.statusText }
          };
          this.errorHandler(errorInfo);
        }
      }
      return response;
    };
  }

  // 设置控制台错误处理器
  private setupConsoleErrorHandler(): void {
    const self = this;
    console.error = (...args: any[]) => {
      if (shouldSample(this.config.sampleRate)) {
        const errorInfo: ErrorInfo = {
          type: 'custom', message: `Console Error: ${args.map(arg => String(arg)).join(' ')}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
          extra: { consoleArgs: args }
        };
        this.errorHandler(errorInfo);
      }
      self.originalConsoleError.apply(console, args);
    };
    console.warn = (...args: any[]) => {
      if (shouldSample(this.config.sampleRate)) {
        const errorInfo: ErrorInfo = {
          type: 'custom', message: `Console Warning: ${args.map(arg => String(arg)).join(' ')}`, timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(),
          extra: { consoleArgs: args }
        };
        this.errorHandler(errorInfo);
      }
      self.originalConsoleWarn.apply(console, args);
    };
  }

  // 手动上报错误
  public reportError(error: Error | string, extra?: Record<string, any>): void {
    if (!shouldSample(this.config.sampleRate)) {
      return; // 不采样则返回
    }
    const errorInfo: ErrorInfo = {
      type: 'custom', message: typeof error === 'string' ? error : error.message, stack: typeof error === 'string' ? '' : formatErrorStack(error),
      timestamp: Date.now(), url: getCurrentUrl(), userAgent: getUserAgent(), extra: extra
    };
    this.errorHandler(errorInfo);
  }

  // 销毁错误监控器，移除所有监听并恢复原型
  public destroy(): void {
    window.removeEventListener('error', this.jsErrorHandler, true);
    window.removeEventListener('error', this.resourceErrorHandler, true);
    window.removeEventListener('unhandledrejection', this.promiseErrorHandler);

    if (this.config.enableConsole) {
      console.error = this.originalConsoleError;
      console.warn = this.originalConsoleWarn;
    }
    XMLHttpRequest.prototype.open = this.originalXHROpen;
    XMLHttpRequest.prototype.send = this.originalXHRSend;
    window.fetch = this.originalFetch;
  }
}
